home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 June / EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso / earcd / comm1 / s342q12.lha / roomb.c < prev    next >
C/C++ Source or Header  |  1996-03-09  |  51KB  |  1,955 lines

  1. /**
  2. *       roomb.c
  3. *
  4. * room code for Citadel bulletin board system
  5. *       History
  6. *
  7. * 87Mar30 HAW  Fix for carrier loss in renameRoom, Invite only room
  8. *   bug, add <L>ogin to novice menu for unlogged
  9. * 86Aug16 HAW  Kill history in here due to space problems.
  10. * 85Jan16 JLS  Fix getText so console starting CR creates blank msg.
  11. * 84Jun28 JLS  Enhancement: Creator of a room is listed in Aide>.
  12. * 84Apr04 HAW  Start upgrade to BDS 1.50a.
  13. * 83Feb26 CrT  bug in makeRoom when out of rooms fixed.
  14. * 83Feb26 CrT  matchString made caseless, normalizeString()
  15. * 83Feb26 CrT  "]" directory prompt, user name before prompts
  16. * 82Dec06 CrT  2.00 release.
  17. * 82Nov02 CrT  Cleanup prior to V1.2 mods.
  18. * 82Nov01 CrT  Proofread for CUG distribution.
  19. * 82Mar27 dvm  conversion to v. 1.4 begun
  20. * 82Mar25 dvm  conversion for TRS-80/Omikron test started
  21. * 81Dec21 CrT  Log file...
  22. * 81Dec20 CrT  Messages...
  23. * 81Dec19 CrT  Rooms seem to be working...
  24. * 81Dec12 CrT  Started.
  25. */
  26. #include "ctdl.h"
  27. /**
  28. *       Contents
  29. *
  30. * CleanEnd()    cleans up end of msg
  31. * editText()    handles the end-of-message-entry menu
  32. * findRoom()    find a free room
  33. * getNumber()   prompt user for a number, limited range
  34. * getString()   read a string in from user
  35. * getText()   reads a message in from user
  36. * getYesNo()    prompts for a yes/no response
  37. * givePrompt()    gives usual "THISROOM>" prompt
  38. * indexRooms()    build RAM index to ctdlroom.sys
  39. * initialArchive()  does initial archive of a room
  40. * insertParagraph() inserts paragraph into message
  41. * makeRoom()    make new room via user dialogue
  42. * matchString()   search for given string
  43. * noteRoom()    enter room into RAM index
  44. * renameRoom()    sysop special to rename rooms
  45. * replaceString()   string-substitute for message entry
  46. * searchForRoom()   auxilary to addToList()
  47. *
  48. * # -- operating system dependent function.
  49. */
  50. char     *public_str = " Public";
  51. char     *private_str = " Private";
  52. char     *perm_str = "Permanent";
  53. char     *temp_str = "Temporary";
  54. char      exChar = '?';
  55. char     *on = "on";
  56. char     *off = "off";
  57. char     *no = "no";
  58. char     *yes = "yes";
  59. char      ShType;
  60. char      MsgEntryType = MSG_ENTRY;
  61. extern SListBase Arch_base;
  62. extern aRoom roomBuf;             /** Room buffer */
  63. extern rTable *roomTab;         /** RAM index   */
  64. extern FILE *roomfl;              /** Room file descriptor   */
  65. extern CONFIG cfg;                  /** Other variables   */
  66. extern MessageBuffer msgBuf;        /** Message buffer   */
  67. extern MessageBuffer tempMess;    /** For held messages   */
  68. extern logBuffer logBuf;          /** Person buffer   */
  69. extern logBuffer logTmp;          /** Person buffer   */
  70. extern NetBuffer netBuf;
  71. extern SListBase Moderators;
  72. extern int masterCount,
  73.           thisRoom,
  74.           thisLog;
  75. extern char remoteSysop;
  76. extern char outFlag;              /** Output flag   */
  77. extern char loggedIn;             /** Logged in?   */
  78. extern char haveCarrier;          /** Have carrier?   */
  79. extern char onConsole;            /** How about on Console?   */
  80. extern char whichIO;              /** Where is I/O?   */
  81. extern char *baseRoom;
  82. extern char heldMess;
  83. extern char echo;
  84. extern char echoChar;
  85. extern char *confirm;
  86. extern int thisNet;
  87. extern char *WRITE_TEXT;
  88. extern FILE *netLog;
  89. static char CCAddFlag;
  90.  
  91. /**
  92.   editText()
  93.   This funcion handles the end-of-message-entry menu.
  94.   return TRUE  to save message to disk, FALSE to abort message, and
  95.   ERROR if user decides to continue
  96. **/
  97. int
  98. editText(char *buf, int lim)
  99. {
  100.   extern char *ALL_LOCALS,
  101.            *WRITE_LOCALS;
  102.   char      c,
  103.            *oldRec;
  104.   int       letter;
  105.   char     *OtherEdit[] =
  106.   {
  107.     "Abort\n", "Continue", "Replace String\n", "Print Formatted\n",
  108.     "Global Replace\n",
  109.     " ", " ", " ", " ", " ", " ", " ", " ",
  110.     " ", " ", " ", " ", " ", " ", " ", ""
  111.  
  112.   };
  113.   static struct
  114.     {
  115.       char     *Save;
  116.       char     *Menu;
  117.       char     *Print;
  118.  
  119.     }
  120.   Displays[] =
  121.   {
  122.     {
  123.       "Saving message...\n", "edit.mnu", NULL
  124.  
  125.     }
  126.     ,
  127.     {
  128.       "Saving file description...\n", "descedit.mnu", "File description"
  129.  
  130.     }
  131.     ,
  132.     {
  133.       "Saving information...\n", "infoedit.mnu", "Information"
  134.  
  135.     }
  136.     ,
  137.     {
  138.       "Saving biography...\n", "infoedit.mnu", "Biography"
  139.  
  140.     }
  141.     ,
  142.  
  143.   };
  144.   ExtraOption(OtherEdit, Displays[MsgEntryType].Save);
  145.   if (MsgEntryType == MSG_ENTRY)
  146.     {
  147.       ExtraOption(OtherEdit, "Hold message for later\n");
  148.       ExtraOption(OtherEdit, "Insert paragraph break\n");
  149.       if (thisRoom == MAILROOM && loggedIn)
  150.         ExtraOption(OtherEdit, "Who else\n");
  151.       if (NetValidate(FALSE) && (thisRoom == MAILROOM ||
  152.                                  roomBuf.rbflags.SHARED))
  153.         ExtraOption(OtherEdit, "N");
  154.  
  155.     }
  156.   else if (MsgEntryType == INFO_ENTRY || MsgEntryType == BIO_ENTRY)
  157.     {
  158.       ExtraOption(OtherEdit, "Insert paragraph break\n");
  159.  
  160.     }
  161.   if (onConsole && cfg.BoolFlags.SysopEditor)
  162.     ExtraOption(OtherEdit, "Outside Editor");
  163.   OtherEditOptions(OtherEdit);
  164.   RegisterThisMenu(Displays[MsgEntryType].Menu, OtherEdit);
  165.   do
  166.     {
  167.       outFlag = IMPERVIOUS;
  168.       doCR();
  169.       if (MsgEntryType == MSG_ENTRY && !logBuf.lbflags.NoPrompt)
  170.         Output_Citadel_Message("FLOORN", (long)roomBuf.rbname , NULL, NULL);
  171.       mPrintf("Editor cmd: ");
  172.       switch ((letter = GetMenuChar()))
  173.         {
  174.           case 'A':
  175.             if (strLen(buf) == 0 || getYesNo("CONFRM"))
  176.               {
  177.                 return FALSE;
  178.  
  179.               }
  180.             break;
  181.           case 'C':
  182.             doCR();
  183.             mPrintf("...%s", CleanEnd(buf));
  184.             return ERROR;
  185.           case 'I':
  186.             insertParagraph(buf, lim);
  187.             break;
  188.           case 'W':
  189.             if (thisRoom == MAILROOM)
  190.               {
  191.                 CCAddFlag = TRUE;
  192.                 getList(CCAddDelete, "Other recipients", CC_SIZE, FALSE);
  193.                 CCAddFlag = FALSE;
  194.                 getList(CCAddDelete, "Users to take off Other recipients",CC_SIZE, FALSE);
  195.  
  196.               }
  197.             break;
  198.           case 'P':
  199.             outFlag = OUTOK;
  200.             if (Displays[MsgEntryType].Print != NULL)
  201.               {
  202.                 doCR();
  203.                 mPrintf("   %s", Displays[MsgEntryType].Print);
  204.                 doCR();
  205.                 mFormat(buf);
  206.  
  207.               }
  208.             else
  209.               printMessage(TRUE);
  210.             break;
  211.           case 'R':
  212.           case 'G':
  213.             replaceString(buf, lim, (letter != 'R'));
  214.             break;
  215.           case 'S':
  216.             if (MsgEntryType == MSG_ENTRY && roomBuf.rbflags.SHARED &&
  217.                 cfg.BoolFlags.netParticipant &&
  218.                 loggedIn &&
  219.                 strLen(msgBuf.mbaddr) == 0 &&
  220.                 logBuf.lbflags.NET_PRIVS)
  221.               {
  222.                 if (getYesNo("SVNETM"))
  223.                   if (!netInfo(TRUE))
  224.                     break;
  225.  
  226.               }
  227.             if (MsgEntryType == MSG_ENTRY && roomBuf.rbflags.ANON && loggedIn)
  228.               {
  229.                 if (!getYesNo("SVANON"))
  230.                   {
  231.                     msgBuf.mbdate[0] = 0;
  232.                     strCpy(msgBuf.mbauth, logBuf.lbname);
  233.  
  234.                   }
  235.  
  236.               }
  237.             return TRUE;
  238.           case 'H':
  239.             if (heldMess)
  240.               {
  241.         Output_Citadel_Message("MSGHLD", NULL, NULL, NULL);
  242.         break;
  243.               }
  244.             Output_Citadel_Message("HOLDIT", NULL, NULL, NULL);
  245.             MoveMsgBuffer(&tempMess, &msgBuf);
  246.             heldMess = TRUE;
  247.             return FALSE;
  248.           case '?':
  249.             if ((MsgEntryType == MSG_ENTRY && roomBuf.rbflags.SHARED || thisRoom == MAILROOM)
  250.                 && cfg.BoolFlags.netParticipant &&
  251.                 loggedIn &&
  252.                 logBuf.lbflags.NET_PRIVS)
  253.               mPrintf(" <N>et toggle\n ");
  254.             if (cfg.BoolFlags.SysopEditor && onConsole)
  255.               mPrintf(" <O>utside Editor\n ");
  256.             if (MsgEntryType == MSG_ENTRY && thisRoom == MAILROOM && loggedIn)
  257.               mPrintf(" <W>ho else (Carbon Copies)\n ");
  258.             ShowOutsideEditors();
  259.             break;
  260.           case 'O':
  261.             OutsideEditor();
  262.             break;
  263.           case 'N':
  264.             if (msgBuf.mbaddr[0])
  265.               {
  266.                 c = msgBuf.mbaddr[0];   /* need to remember this */
  267.                 msgBuf.mbaddr[0] = 0;   /* zero for getRecipient */
  268.                 mPrintf("\bormal message\n ");
  269.                 oldRec = strdup(msgBuf.mbto);   /* getRec might zero mbto */
  270.                 if (getRecipient())
  271.                   {
  272.                     msgBuf.mboname[0] = 0;
  273.                     msgBuf.mbdomain[0] = 0;
  274.  
  275.                   }
  276.                 else
  277.                   {
  278.                     msgBuf.mbaddr[0] = c;       /* failed, restore address */
  279.                     strCpy(msgBuf.mbto, oldRec);        /** restore recip        */
  280.  
  281.                   }
  282.                 free(oldRec);
  283.  
  284.               }
  285.             else
  286.               {
  287.                 mPrintf("\betwork message\n ");
  288.                 netInfo(TRUE);
  289.  
  290.               }
  291.             break;
  292.           default:
  293.             RunRemoteEditor(letter);
  294.  
  295.         }
  296.  
  297.     }
  298.   while (onLine());
  299.   if (MsgEntryType == MSG_ENTRY && loggedIn)
  300.     SaveInterrupted(&msgBuf);
  301.   return FALSE;
  302.  
  303. }
  304. /*
  305.  * * CCAddDelete() * * This function handles adding or deleting Other
  306.  * Recipients.
  307.  */
  308. int
  309. CCAddDelete(char *name)
  310. {
  311.   label     person;
  312.   char      buf[CC_SIZE],
  313.             isdomain;
  314.   char      system[(2 * NAMESIZE) + 10],
  315.            *domain;
  316.   int       result,
  317.             cost;
  318.  
  319.   result = SepNameSystem(name, person, system, &netBuf);
  320.   strCpy(buf, name);
  321.   if (result == IS_SYSTEM)
  322.     sPrintf(buf, "%s @%s", person, system);
  323.   isdomain = (domain = strchr(system, '_')) != NULL;
  324.   if (isdomain)
  325.     domain += 2;        /*
  326.                          * formats to "system _ domain", so we cheat
  327.                          */
  328.   if (!CCAddFlag)
  329.     {
  330.  /*
  331.   * Take it off the list
  332.   */
  333.       KillData(&msgBuf.mbCC, buf);
  334.  
  335.     }
  336.   else
  337.     {
  338.       switch (result)
  339.         {
  340.           case BAD_FORMAT:
  341.       Output_Citadel_Message("NOTFND", (long)name , NULL, NULL);
  342.             break;
  343.           case SYSTEM_IS_US:
  344.             break;      /*
  345.                          * error message handled in SepNameSystem
  346.                          */
  347.           case NO_SYSTEM:
  348.       Output_Citadel_Message("NOTFND", (long)name , NULL, NULL);
  349.             break;
  350.           case IS_SYSTEM:
  351.             cost = (isdomain) ? FindCost(domain) : !netBuf.nbflags.local;
  352.             if (logBuf.credit < cost)
  353.               {
  354.                 if (HalfSysop())
  355.                   {
  356.                     logBuf.credit += cost;
  357.  
  358.                   }
  359.                 else
  360.                   {
  361.         Output_Citadel_Message("NOCRDT", NULL, NULL, NULL);
  362.                     break;
  363.  
  364.                   }
  365.  
  366.               }
  367.             else if (!logBuf.lbflags.NET_PRIVS)
  368.               {
  369.         Output_Citadel_Message("NONETP", NULL, NULL, NULL);
  370.         break;
  371.  
  372.               }
  373.             AddData(&msgBuf.mbCC, strdup(buf), NULL, TRUE);
  374.             break;
  375.           case NOT_SYSTEM:
  376.             if (PersonExists(name) == ERROR)
  377.         Output_Citadel_Message("NOTFND", (long)name , NULL, NULL);
  378.             else
  379.               {
  380.                 if (strCmpU(name, logBuf.lbname) == SAMESTRING)
  381.        Output_Citadel_Message("NOSELF", NULL, NULL, NULL);
  382.                 else if (!msgBuf.mbaddr[0] &&
  383.                          strCmpU(name, msgBuf.mbto) == SAMESTRING)
  384.        Output_Citadel_Message("NODUPT", NULL, NULL, NULL);
  385.                 else
  386.                   AddData(&msgBuf.mbCC, strdup(name), NULL, TRUE);
  387.  
  388.               }
  389.             break;
  390.  
  391.         }
  392.  
  393.     }
  394.   return TRUE;
  395.  
  396. }
  397. /*
  398.  * * SepNameSystem() * * This will parse an Other Recipient spec.
  399.  */
  400. char
  401. SepNameSystem(char *string, char *person, char *system, NetBuffer * buf)
  402. {
  403.   char     *c;
  404.   label     domain;
  405.   char      dup,
  406.             work[150];            /*
  407.  
  408.                                    *
  409.                                    * * should be sufficient
  410.                                    */
  411.   int       slot;
  412.  
  413.   strCpy(work, string);
  414.   if (cfg.BoolFlags.debug)
  415.     splitF(NULL, " SepNameSystem:%s\n", string);
  416.   if ((c = strchr(work, '@')) == NULL)
  417.     {
  418.       if (strLen(work) >= NAMESIZE)
  419.         return BAD_FORMAT;
  420.       strCpy(person, string);
  421.       return (char)NOT_SYSTEM;
  422.  
  423.     }
  424.   *c++ = 0;
  425.   NormStr(work);
  426.   NormStr(c);
  427.   if (cfg.BoolFlags.debug)
  428.     splitF(NULL, "        person:%s\n", work);
  429.   if (cfg.BoolFlags.debug)
  430.     splitF(NULL, "        System:%s\n", c);
  431.   if (strLen(c) >= NAMESIZE * 2 || strLen(work) >= NAMESIZE)
  432.     return (char)BAD_FORMAT;
  433.   strCpy(system, c);
  434.   strCpy(person, work);
  435.   if (buf == NULL)
  436.     return (char)IS_SYSTEM;     /* very minor cheat - see CTDL.C */
  437.   if (cfg.BoolFlags.debug)
  438.     splitF(NULL, "    Search for:%s\n", system);
  439.   if ((slot = searchNameNet(c, buf)) != ERROR)
  440.     {
  441.  /*
  442.   * try secondary lists
  443.   */
  444.       strCpy(system, buf->netName);     /*
  445.                                          * get "real" name
  446.                                          */
  447.       if (cfg.BoolFlags.debug)
  448.         splitF(NULL, "       found:%s\n", system);
  449.       if (buf->nbflags.local || buf->nbflags.RouteLock)
  450.         {
  451.           return (char)IS_SYSTEM;
  452.  
  453.         }
  454.  
  455.     }
  456.   if (cfg.BoolFlags.debug)
  457.     splitF(NULL, "  Still Search:%s\n", system);
  458.   if (SystemInSecondary(c, domain, &dup))
  459.     {
  460.       if (dup)
  461.         {
  462.      /*
  463.       * oops
  464.       */
  465.           if (slot == ERROR)
  466.        Output_Citadel_Message("DUPDOM", (long)c, NULL, NULL);
  467.           return (char) ((slot == ERROR) ? NO_SYSTEM : IS_SYSTEM);
  468.  
  469.         }
  470.       if (strCmpU(domain, cfg.nodeDomain + cfg.codeBuf) == SAMESTRING &&
  471.           (strCmpU(c, cfg.nodeName + cfg.codeBuf) == SAMESTRING ||
  472.            strCmpU(c, UseNetAlias(cfg.nodeName + cfg.codeBuf, TRUE))
  473.            == SAMESTRING))
  474.         {
  475.           mPrintf("Hey, that's this system!\n ");
  476.           return (char) SYSTEM_IS_US;
  477.  
  478.         }
  479.       sPrintf(system, "%s _ %s", c, domain);
  480.       return (char)IS_SYSTEM;
  481.  
  482.     }
  483.   if (cfg.BoolFlags.debug)
  484.     splitF(NULL, "(%d)= SepNameSystem(%s,%s,%s,%x)\n ", slot, string, person, system, buf);
  485.   return (char)((slot == ERROR) ? NO_SYSTEM : IS_SYSTEM);
  486.  
  487. }
  488. /*
  489.  * * findRoom() * * This function finds and returns the # of a free room
  490.  * slot if possible, * else ERROR.
  491.  */
  492. int
  493. findRoom()
  494. {
  495.   int       roomRover;
  496.  
  497.   for (roomRover = 0; roomRover < MAXROOMS; roomRover++)
  498.     {
  499.       if (roomTab[roomRover].rtflags.INUSE == 0)
  500.         return roomRover;
  501.  
  502.     }
  503.   return ERROR;
  504.  
  505. }
  506. /*
  507.  * * getNumber() * * This prompts for a number in (bottom, top) range.
  508.  */
  509. long
  510. getNumber(char *prompt, long bottom, long top)
  511. {
  512.   long      try;
  513.   char      numstring[NAMESIZE];
  514.  
  515.   do
  516.     {
  517.       if (!gotCarrier())
  518.         {
  519.           haveCarrier = FALSE;
  520.         };
  521.       Output_Citadel_Message(prompt,NULL, NULL, NULL);
  522.       getString("", numstring, NAMESIZE, 0);
  523.       try = atol(numstring);
  524.       if( try < bottom || try > top)
  525.         Output_Citadel_Message("RANGEV",bottom, top, NULL);
  526.     }
  527.   while ((try < bottom || try > top) && onLine());
  528.   return (long) try;
  529.  
  530. }
  531. /*
  532.  * * getString() * * This gets a string from the user.
  533.  */
  534. int
  535. getString(char *prompt, char *buf, int lim, int Flags)
  536. {
  537.   int       toReturn;
  538.   extern int crtColumn;
  539.   char      oldEcho;
  540.  
  541.   outFlag = IMPERVIOUS;
  542.   if( prompt != NULL )
  543.     {
  544.     if (strLen(prompt) > 0)
  545.       {
  546.       doCR();
  547.       Output_Citadel_Message(prompt,NULL,NULL,NULL);
  548.       };
  549.     };
  550.   oldEcho = echo;
  551.   if (Flags & NO_ECHO)
  552.     {
  553.       echo = NEITHER;
  554.       echoChar = 'X';
  555.  
  556.     }
  557.   outFlag = OUTOK;
  558.   Flags |= CR_ON_ABORT;
  559.   if ((toReturn = BlindString(buf, lim, Flags, iChar, oChar, 0)) == BACKED_OUT)
  560.     return toReturn;
  561.   echo = oldEcho;
  562.   crtColumn = 1;
  563.   return toReturn;
  564.  
  565. }
  566. /*
  567.  * * BlindString() * * Gets a string blind to source and sink.
  568.  */
  569. int
  570. BlindString(char *buf, int lim, int Flags,
  571.             char      (*input) (void), void (*output) (char c), char Echo)
  572. {
  573.   char      c;
  574.   int       i;
  575.  
  576.   i = 0;
  577.   while (onLine() && (c = (*input) (), c != NEWLINE && c != '\r' && i < lim))
  578.     {
  579.       if (!gotCarrier())
  580.         {
  581.           haveCarrier = FALSE;
  582.         };
  583.       if (Echo)
  584.         (*output) (c);
  585.  /*
  586.   * handle delete chars:
  587.   */
  588.       if (c == BACKSPACE)
  589.         {
  590.           (*output) (' ');
  591.           (*output) (BACKSPACE);
  592.           if (i > 0)
  593.             i--;
  594.           else if (Flags & BS_VALID)
  595.             {
  596.               return BACKED_OUT;
  597.  
  598.             }
  599.           else
  600.             {
  601.               (*output) (' ');
  602.               (*output) (BELL);
  603.  
  604.             }
  605.  
  606.         }
  607.       else if (c)
  608.         buf[i++] = c;
  609.       if (i >= lim)
  610.         {
  611.           (*output) (BELL);
  612.           (*output) (BACKSPACE);
  613.           i--;
  614.  
  615.         }
  616.  /*
  617.   * kludge to return immediately on single '?':
  618.   */
  619.       if ((Flags & QUEST_SPECIAL) && *buf == exChar)
  620.         {
  621.           if ((Flags & CR_ON_ABORT))
  622.             doCR();
  623.      /*
  624.       * i--;
  625.       */
  626.           break;
  627.  
  628.         }
  629.  
  630.     }
  631.   buf[i] = '\0';
  632.   return GOOD_SELECT;
  633.  
  634. }
  635. /*
  636.  * * getText() * * This manages reading a message from the user. * Returns
  637.  * TRUE if user decides to save it, else FALSE (whether held or * aborted).
  638.  */
  639. char
  640. getText(int uploading)
  641. {
  642.   extern PROTO_TABLE Table[];
  643.   extern char *R_SH_MARK,
  644.             EndWithCR;
  645.   int       i,
  646.             toReturn;
  647.  
  648. /*
  649.  * msgBuf.mbtext[-1] = NEWLINE;
  650.  */
  651.   if (uploading == ASCII)
  652.     {
  653.     Output_Citadel_Message("UPLDAS", NULL, NULL, NULL);
  654.       outFlag = OUTOK;
  655.       if (msgBuf.mbtext[0])
  656.         CleanEnd(msgBuf.mbtext);
  657.       EndWithCR = FALSE;
  658.       printMessage(TRUE);
  659.       EndWithCR = TRUE;
  660.       outFlag = OUTOK;
  661.  
  662.     }
  663.   else
  664.     {
  665.       if (!expert)
  666.         tutorial(Table[uploading].UpBlbName, TRUE);
  667.       if (!getYesNo("RDYBEG"))
  668.         return FALSE;
  669.       masterCount = 0;
  670.       if (uploading <= TOP_PROTOCOL)
  671.         {
  672.           if (Reception(uploading, putBufChar) != TRAN_SUCCESS)
  673.             return FALSE;
  674.  
  675.         }
  676.       else
  677.         {
  678.           if (EatExtMessage(uploading) != TRAN_SUCCESS)
  679.             return FALSE;
  680.  
  681.         }
  682.  
  683.     }
  684.   toReturn = GetBalance(uploading, msgBuf.mbtext, MAXTEXT);
  685.   if (toReturn == TRUE)
  686.     {
  687.  /*
  688.   * Filter null messages
  689.   */
  690.       toReturn = FALSE;
  691.       for (i = 0; msgBuf.mbtext[i] != 0 && !toReturn; i++)
  692.         toReturn = (msgBuf.mbtext[i] > ' ' && msgBuf.mbtext[i] < 127);
  693.  
  694.     }
  695.   return (char) toReturn;
  696.  
  697. }
  698. /*
  699.  * * GetBalance() * * This function gets the balance of text for a message.
  700.  */
  701. char
  702. GetBalance(int uploading, char *buf, int size)
  703. {
  704.   char      c,
  705.             beeped = FALSE;
  706.   int       i,
  707.             toReturn;
  708.  
  709.   do
  710.     {
  711.       i = strLen(buf);
  712.       if (uploading == ASCII)
  713.         while (
  714.                 !(
  715.                    (c = iChar()) == NEWLINE &&
  716.                    (i == 0 || buf[i - 1] == NEWLINE)
  717.                 )
  718.                 && i < size - 1
  719.                 && onLine()
  720.           )
  721.           {
  722.             if (c != BACKSPACE)
  723.               {
  724.                 if (c == ESC)
  725.                   {
  726.                     if (iChar() == '[')
  727.                       {
  728.                         iChar();
  729.                         continue;       /*
  730.                                          * arrow key.
  731.                                          */
  732.  
  733.                       }
  734.  
  735.                   }
  736.                 if (c != 0 && c != XOFF && c != XON)
  737.                   buf[i++] = c;
  738.                 if (i > size - 80 && !beeped)
  739.                   {
  740.                     beeped = TRUE;
  741.                     oChar(BELL);
  742.  
  743.                   }
  744.  
  745.               }
  746.             else
  747.               {
  748.            /*
  749.             * handle delete chars:
  750.             */
  751.                 oChar(' ');
  752.                 oChar(BACKSPACE);
  753.                 if (i > 0 && buf[i - 1] != NEWLINE)
  754.                   i--;
  755.                 else
  756.                   oChar(BELL);
  757.  
  758.               }
  759.             buf[i] = 0;         /*
  760.                                  * null to terminate message
  761.                                  */
  762.             if (i == size - 1)
  763.               {
  764.                 mPrintf(" buffer overflow\n ");
  765.                 while (receive(2) != ERROR)
  766.                   ;
  767.  
  768.               }
  769.  
  770.           }
  771.       if (i == size - 1)
  772.         buf[--i] = 0;   /*
  773.                          * fixes an odd buffer overflow problem
  774.                          */
  775.  /*
  776.   * couldn't edit the message otherwise
  777.   */
  778.       toReturn = editText(buf, size - 1);
  779.       uploading = ASCII;
  780.  
  781.     }
  782.   while ((toReturn == ERROR) && onLine());
  783.   return (char) toReturn;
  784.  
  785. }
  786. /*
  787.  * * getYesNo() * * This prompts for a yes/no response and gets it.
  788.  */
  789. char
  790. getYesNo(char *prompt)
  791. {
  792.   int       toReturn;
  793.   extern char ConOnly;
  794.   char      (*input) (void);
  795.  
  796.   input = ConOnly ? getCh : iChar;
  797.   for (doCR(), toReturn = ERROR; toReturn == ERROR && onLine();)
  798.     {
  799.       outFlag = IMPERVIOUS;
  800.       Output_Citadel_Message(prompt,NULL,NULL,NULL);
  801.       switch (toUpper((*input) ()))
  802.         {
  803.           case 'Y':
  804.             toReturn = TRUE;
  805.             break;
  806.           case 'N':
  807.             toReturn = FALSE;
  808.             break;
  809.           case '\0':
  810.             toReturn = TRUE;
  811.             break;
  812.  
  813.         };
  814.       doCR();
  815.  
  816.     }
  817.   outFlag = OUTOK;
  818.   return (char) toReturn;
  819.  
  820. }
  821. /*
  822.  * * givePrompt() * * This function simply prints the usual "CURRENTROOM>"
  823.  * prompt -- not as simple * as it may seem.
  824.  */
  825. void
  826. givePrompt()
  827. {
  828.   outFlag = IMPERVIOUS;
  829.   doCR();
  830.   ScreenUser();
  831.   if (!loggedIn)
  832.     Output_Citadel_Message("NOTLGN", NULL, NULL, NULL);
  833.   Output_Citadel_Message("PROMPT"
  834.          ,(long)( (thisRoom == MAILROOM || loggedIn ||cfg.BoolFlags.unlogReadOk) ?
  835.              "<N>ew messages" : " ")
  836.          ,(long)( (thisRoom == MAILROOM || loggedIn ||cfg.BoolFlags.unlogEnterOk)?
  837.              "<E>nter" : " " )
  838.          , NULL);
  839.  
  840.   if (!expert) doCR();
  841.   if (roomBuf.rbflags.READ_ONLY)
  842.     {
  843.     Output_Citadel_Message("READOL", NULL, NULL, NULL);
  844.     doCR();
  845.  
  846.     }
  847.   mPrintf("%s ", formRoom(thisRoom, FALSE, TRUE));
  848.   if (strCmp(roomBuf.rbname, roomTab[thisRoom].rtname) != SAMESTRING)
  849.     {
  850.       printf("thisRoom=%d, rbname=-%s-, rtname=-%s-\n", thisRoom,
  851.              roomBuf.rbname, roomTab[thisRoom].rtname);
  852.       crashout("Dependent variables mismatch!");
  853.  
  854.     }
  855.   outFlag = OUTOK;
  856.  
  857. }
  858. /*
  859.  * * indexRooms() * * This function will try to find and free an empty room.
  860.  */
  861. void
  862. indexRooms()
  863. {
  864.   int       goodRoom,
  865.             slot;
  866.  
  867.   for (slot = 0; slot < MAXROOMS; slot++)
  868.     {
  869.       if (roomTab[slot].rtflags.INUSE == 1)
  870.         {
  871.           goodRoom = FALSE;
  872.           if (roomTab[slot].rtlastMessage > cfg.oldest ||
  873.               roomTab[slot].rtflags.PERMROOM == 1)
  874.             {
  875.               goodRoom = TRUE;
  876.  
  877.             }
  878.           if (!goodRoom)
  879.             {
  880.               getRoom(slot);
  881.               KillInfo(roomTab[slot].rtname);
  882.               roomBuf.rbflags.INUSE = 0;
  883.               putRoom(slot);
  884.               strCat(msgBuf.mbtext, roomBuf.rbname);
  885.               strCat(msgBuf.mbtext, "> ");
  886.               noteRoom();
  887.  
  888.             }
  889.  
  890.         }
  891.  
  892.     }
  893.  
  894. }
  895.  
  896. /*
  897.  * * insertParagraph() * * This inserts a paragraph (CR/Space) into a
  898.  * message. * (By Jay Johnson of The Phoenix)
  899.  */
  900. void
  901. insertParagraph(char *buf, int lim)
  902. {
  903.   char      oldString[2 * SECTSIZE];
  904.   char     *loc,
  905.            *textEnd;
  906.   char     *pc;
  907.   int       length;
  908.  
  909.   for (textEnd = buf, length = 0; *textEnd; length++, textEnd++) ;
  910.   if (lim - length < 3)
  911.     {
  912.     Output_Citadel_Message("NORMMG", NULL, NULL, NULL);
  913.     return;
  914.  
  915.     }
  916.   getString("GETPAR", oldString, (2 * SECTSIZE), 0);
  917.   if ((loc = matchString(buf, oldString, textEnd)) == NULL)
  918.     {
  919.     Output_Citadel_Message("NOTFND", NULL, NULL, NULL);
  920.     return;
  921.  
  922.     }
  923.   for (pc = textEnd; pc >= loc; pc--)
  924.     {
  925.       *(pc + 2) = *pc;
  926.  
  927.     }
  928.   *loc++ = '\n';
  929.   *loc = ' ';
  930.  
  931. }
  932. /*
  933.  * * makeRoom() * * This function constructs a new room via dialogue with
  934.  * user.
  935.  */
  936. void
  937. makeRoom()
  938. {
  939.   label     nm,
  940.             oldName;
  941.   int       CurrentFloor,
  942.             oldRoom;
  943.  
  944.   CurrentFloor = thisFloor;
  945.   oldRoom = thisRoom;
  946. /*
  947.  * update lastMessage for current room:
  948.  */
  949.   logBuf.lbgen[thisRoom] = roomBuf.rbgen << GENSHIFT;
  950.   strCpy(oldName, roomBuf.rbname);
  951.   if ((thisRoom = findRoom()) == ERROR)
  952.     {
  953.       indexRooms();     /*
  954.                          * try and reclaim an empty room
  955.                          */
  956.       if ((thisRoom = findRoom()) == ERROR)
  957.         {
  958.     Output_Citadel_Message("NORMMG", NULL, NULL, NULL);
  959.      /*
  960.       * may have reclaimed old room, so:
  961.       */
  962.           if (roomExists(oldName) == ERROR)
  963.             strCpy(oldName, baseRoom);
  964.           getRoom(roomExists(oldName));
  965.           return;
  966.  
  967.         }
  968.  
  969.     }
  970.   getNormStr("ROOMNM", nm, NAMESIZE, 0);
  971.   if (strLen(nm) == 0)
  972.     {
  973.       if (roomExists(oldName) == ERROR)
  974.         strCpy(oldName, baseRoom);
  975.       getRoom(roomExists(oldName));
  976.       return;
  977.  
  978.     }
  979.   if (roomExists(nm) >= 0)
  980.     {
  981.     Output_Citadel_Message("ALRDYE", (long)nm, NULL, NULL);
  982.  /*
  983.   * may have reclaimed old room, so:
  984.   */
  985.       if (roomExists(oldName) == ERROR)
  986.         strCpy(oldName, baseRoom);
  987.       getRoom(roomExists(oldName));
  988.       return;
  989.  
  990.     }
  991.   if (!expert)
  992.     tutorial("newroom.blb", TRUE);
  993.   zero_struct(roomBuf.rbflags);
  994.   roomBuf.rbflags.INUSE = TRUE;
  995.   if (getYesNo("MKRMPD"))
  996.     roomBuf.rbflags.PUBLIC = TRUE;
  997.   else
  998.     roomBuf.rbflags.PUBLIC = FALSE;
  999.   mPrintf("'%s', a %s room", nm,
  1000.           roomBuf.rbflags.PUBLIC == 1 ? "public" : "private"
  1001.     );
  1002.   if (!getYesNo("MKRMIS"))
  1003.     {
  1004.  /*
  1005.   * may have reclaimed old room, so:
  1006.   */
  1007.       if (roomExists(oldName) == ERROR)
  1008.         strCpy(oldName, baseRoom);
  1009.       getRoom(roomExists(oldName));
  1010.       return;
  1011.  
  1012.     }
  1013.   else if (roomExists(oldName) == ERROR)
  1014.     oldRoom = -1;       /*
  1015.                          * in case
  1016.                          */
  1017.   strCpy(roomBuf.rbname, nm);
  1018.   memset(roomBuf.msg, 0, MSG_BULK);     /*
  1019.                                          * mark all slots empty
  1020.                                          */
  1021.   roomBuf.rbgen = (roomTab[thisRoom].rtgen + 1) % MAXGEN;
  1022.   roomBuf.rbFlIndex = CurrentFloor;
  1023.   KillData(&Moderators, NtoStrInit(thisRoom, "", 0, TRUE));
  1024.   WriteAList(&Moderators, "ctdlmodr.sys", WrtNtoStr);
  1025.   KillData(&Arch_base, NtoStrInit(thisRoom, "", 0, TRUE));
  1026.   WriteAList(&Arch_base, "ctdlarch.sys", WrtArchRec);
  1027.   noteRoom();   /*
  1028.                  * index new room
  1029.                  */
  1030.   RoomSys(thisRoom);    /*
  1031.                          * initialize directory area
  1032.                          */
  1033.   putRoom(thisRoom);
  1034.   ZeroMsgBuffer(&msgBuf);
  1035.   Output_Citadel_Message("ENTINF",(long)roomBuf.rbname, NULL, NULL);
  1036.   EditInfo();   /*
  1037.                  * creator gets to set information
  1038.                  */
  1039.   msgBuf.mboname[0] = 0;        /*
  1040.                                  * icky kludge
  1041.                                  */
  1042.   if (strLen(msgBuf.mbtext) != 0 &&
  1043.       getYesNo("MKINFO"))
  1044.     procMessage(ASCII, FALSE);
  1045. /*
  1046.  * update logBuf:
  1047.  */
  1048.   logBuf.lbgen[thisRoom] = roomBuf.rbgen << GENSHIFT;
  1049.   ZeroMsgBuffer(&msgBuf);
  1050.   sPrintf(msgBuf.mbtext, "%s created by %s.", formRoom(thisRoom, FALSE, FALSE),
  1051.           logBuf.lbname);
  1052.   aideMessage(NULL, FALSE);
  1053.   roomTab[thisRoom].rtlastNet = 0l;
  1054.   if (oldRoom != -1)
  1055.     UngotoMaintain(oldRoom);
  1056.  
  1057. }
  1058. /*
  1059.  * * WriteAList() * * This writes a Num->String list to disk.
  1060.  */
  1061. void
  1062. WriteAList(SListBase * base, char *fn, void (*func) ())
  1063. {
  1064.   SYS_FILE  name;
  1065.   extern FILE *upfd;
  1066.  
  1067.   makeSysName(name, fn, &cfg.roomArea);
  1068.   if ((upfd = safeopen(name, WRITE_TEXT)) != NULL)
  1069.     {
  1070.       RunList(base, func);
  1071.       fclose(upfd);
  1072.  
  1073.     }
  1074.   else
  1075.     Output_Citadel_Message("NOOPEN", (long)name, NULL, NULL);
  1076. }
  1077. /*
  1078.  * * matchString() * * This searches for match to given string.  Runs
  1079.  * backward through buffer so * we get most recent error first.  Returns loc
  1080.  * of match, else NULL.
  1081.  */
  1082. char     *
  1083. matchString(char *buf, char *pattern, char *bufEnd)
  1084. {
  1085.   char     *loc,
  1086.            *pc1,
  1087.            *pc2;
  1088.   char      foundIt;
  1089.  
  1090.   for (loc = bufEnd, foundIt = FALSE; !foundIt && --loc >= buf;)
  1091.     {
  1092.       for (pc1 = pattern, pc2 = loc, foundIt = TRUE; *pc1 && foundIt;)
  1093.         {
  1094.           if (!(toUpper(*pc1++) == toUpper(*pc2++)))
  1095.             foundIt = FALSE;
  1096.  
  1097.         }
  1098.  
  1099.     }
  1100.   return foundIt ? loc : NULL;
  1101.  
  1102. }
  1103. /*
  1104.  * * getNormStr() * * This function gets a string and deletes leading &
  1105.  * trailing blanks etc.
  1106.  */
  1107. int
  1108. getNormStr(char *prompt, char *s, int size, int Flags)
  1109. {
  1110.   int       toReturn;
  1111.  
  1112.   if ((toReturn = getString(prompt, s, size, Flags)) != BACKED_OUT)
  1113.     if (*s != exChar && *s != '\0')
  1114.       NormStr(s);
  1115.   return toReturn;
  1116.  
  1117. }
  1118. /*
  1119.  * * noteRoom() * * This will enter a room into RAM index array.
  1120.  */
  1121. void
  1122. noteRoom()
  1123. {
  1124.   int       i;
  1125.   MSG_NUMBER last;
  1126.  
  1127.   last = 0l;
  1128. #ifdef NORMAL_MESSAGES
  1129.   for (i = 0; i < ((thisRoom == MAILROOM) ? MAILSLOTS : MSGSPERRM); i++)
  1130.     {
  1131.       if (roomBuf.msg[i].rbmsgNo > last &&
  1132.           roomBuf.msg[i].rbmsgNo <= cfg.newest)
  1133.         {
  1134.           last = roomBuf.msg[i].rbmsgNo;
  1135.  
  1136.         }
  1137.  
  1138.     }
  1139. #else
  1140.   for (i = 0; i < ((thisRoom == MAILROOM) ? MAILSLOTS : MSGSPERRM); i++)
  1141.     {
  1142.       if ((roomBuf.msg[i].rbmsgNo & (~S_MSG_MASK)) &&
  1143.           (roomBuf.msg[i].rbmsgNo & S_MSG_MASK) > cfg.oldest)
  1144.         last = S_MSG_MASK;
  1145.       if ((roomBuf.msg[i].rbmsgNo & S_MSG_MASK) > last &&
  1146.           (roomBuf.msg[i].rbmsgNo & S_MSG_MASK) <= cfg.newest)
  1147.         {
  1148.           last = (roomBuf.msg[i].rbmsgNo & S_MSG_MASK);
  1149.  
  1150.         }
  1151.  
  1152.     }
  1153. #endif
  1154.   roomTab[thisRoom].rtlastMessage = last;
  1155.   strCpy(roomTab[thisRoom].rtname, roomBuf.rbname);
  1156.   roomTab[thisRoom].rtgen = roomBuf.rbgen;
  1157.   memcpy(&roomTab[thisRoom].rtflags, &roomBuf.rbflags,
  1158.         (long)sizeof roomBuf.rbflags);
  1159.   roomTab[thisRoom].rtShareType = roomBuf.rbShareType;
  1160.   roomTab[thisRoom].rtFlIndex = roomBuf.rbFlIndex;
  1161.  
  1162. }
  1163. char      DoWritePrivs;
  1164.  
  1165. /*
  1166.  * * renameRoom() * * This is a sysop special fn -- it handles all room
  1167.  * editing. * Returns:  TRUE on success else FALSE.
  1168.  */
  1169. char
  1170. renameRoom()
  1171. {
  1172.   int       c,
  1173.             r;
  1174.   char     *buffer,
  1175.             wasRO,
  1176.             wasShared,
  1177.             wasAnon,
  1178.            *save,
  1179.             workbuf[200];
  1180.   extern char *APrivateRoom;
  1181.   char      doAideMessage = 0;    /*
  1182.  
  1183.                                    *
  1184.                                    * * Counter to determine if aide msg
  1185.                                    * needed
  1186.                                    */
  1187.   extern char *WRITE_TEXT;
  1188.   extern FILE *upfd;
  1189.   char     *RoomEditOpts[] =
  1190.   {
  1191.     "X\bExit room editing\n", "Name change\n", "Temporary room\n",
  1192.     "Private setting\n", "Lure users to room\n", "Only Invitational\n",
  1193.     "Innominate status\n", "Values\n", "Withdraw Invitations\n",
  1194.     "Edit information\n", "Read only\n",
  1195.     " ", " ", " ", " ", " ", " ", " ", " ", ""
  1196.  
  1197.   };
  1198.  
  1199.   if (thisRoom == LOBBY || thisRoom == MAILROOM || thisRoom == AIDEROOM)
  1200.     {
  1201.     Output_Citadel_Message("WARNSP", NULL, NULL, NULL);
  1202.     if (!getYesNo("ALLOWS"))
  1203.       return FALSE;
  1204.  
  1205.     };
  1206.   wasShared = roomBuf.rbflags.SHARED;
  1207.   wasAnon = roomBuf.rbflags.ANON;
  1208.   wasRO = roomBuf.rbflags.READ_ONLY;
  1209.   ZeroMsgBuffer(&msgBuf);
  1210.   sPrintf(msgBuf.mbtext, "%s, formerly ", formRoom(thisRoom, FALSE, FALSE));
  1211.   formatSummary(lbyte(msgBuf.mbtext), FALSE);
  1212.   buffer = strdup(msgBuf.mbtext);
  1213.   if (HalfSysop())
  1214.     {
  1215.       ExtraOption(RoomEditOpts, "Archive status\n");
  1216.       ExtraOption(RoomEditOpts, "Directory status\n");
  1217.       if (cfg.BoolFlags.netParticipant)
  1218.         {
  1219.           ExtraOption(RoomEditOpts, "Backbone setting\n");
  1220.           ExtraOption(RoomEditOpts, "Shared room\n");
  1221.  
  1222.         }
  1223.       if (roomBuf.rbflags.ISDIR)
  1224.         {
  1225.           ExtraOption(RoomEditOpts, "U");
  1226.  
  1227.         }
  1228.       if (cfg.BoolFlags.netParticipant && roomBuf.rbflags.ISDIR)
  1229.         {
  1230.           ExtraOption(RoomEditOpts, "Z\bNetwork Downloadable\n");
  1231.  
  1232.         }
  1233.  
  1234.     }
  1235.   if (aide)
  1236.     ExtraOption(RoomEditOpts, "Moderator setting\n");
  1237.   RegisterThisMenu(HalfSysop()? "rooms.mnu" : "rooma.mnu", RoomEditOpts);
  1238.   c = 0;        /*
  1239.                  * Init
  1240.                  */
  1241.   while (c != 'X' && onLine())
  1242.     {
  1243.       mPrintf("\n Room Editing Command: ");
  1244.       c = GetMenuChar();
  1245.       switch (c)
  1246.         {
  1247.           case 'X':
  1248.             break;
  1249.           case 'A':
  1250.             doAideMessage++;
  1251.             roomBuf.rbflags.ARCHIVE = getYesNo("ACTARC");
  1252.             if (roomBuf.rbflags.ARCHIVE)
  1253.               {
  1254.                 getString("EFILEN", workbuf, (sizeof workbuf) - 1, 0);
  1255.                 if (strLen(workbuf) == 0)
  1256.                   {
  1257.                     roomBuf.rbflags.ARCHIVE = FALSE;
  1258.                     break;
  1259.  
  1260.                   }
  1261.                 c = (int) getNumber("ROLLOV", -1, 32000);
  1262.                 AddData(&Arch_base, NtoStrInit(thisRoom, workbuf, c, FALSE),
  1263.                         NULL, TRUE      /*
  1264.                                          * kill duplicates
  1265.                                          */ );
  1266.                 WriteAList(&Arch_base, "ctdlarch.sys", WrtArchRec);
  1267.                 if (getYesNo("INITAR"))
  1268.                   initialArchive(workbuf);
  1269.  
  1270.               }
  1271.             break;
  1272.           case 'N':
  1273.             getNormStr("ROOMNM", workbuf, NAMESIZE, 0);
  1274.             r = roomExists(workbuf);
  1275.             if (r >= 0 && r != thisRoom)
  1276.               {
  1277.                 mPrintf("A %s exists already!\n", workbuf);
  1278.                 break;
  1279.  
  1280.               }
  1281.             else
  1282.               {
  1283.                 ChangeInfoName(workbuf);
  1284.                 strCpy(roomBuf.rbname, workbuf);        /*
  1285.                                                          * also in room
  1286.                                                          * itself
  1287.                                                          */
  1288.                 doAideMessage++;
  1289.  
  1290.               }
  1291.             if (!getYesNo("EDIFRM"))
  1292.               break;
  1293.           case 'E':
  1294.             save = strdup(msgBuf.mbtext);
  1295.             EditInfo();
  1296.             RegisterThisMenu(SomeSysop()? "rooms.mnu" : "rooma.mnu", RoomEditOpts);
  1297.             strcpy(msgBuf.mbtext, save);
  1298.             free(save);
  1299.             break;
  1300.           case 'B':
  1301.             if (!roomBuf.rbflags.SHARED)
  1302.               {
  1303.         Output_Citadel_Message("NOTSHR", NULL, NULL, NULL);
  1304.                     break;
  1305.  
  1306.               }
  1307.             roomBuf.rbShareType = (getYesNo("SYSBKN")) ?
  1308.               BACKBONE : PEON;
  1309.             if (roomBuf.rbShareType == BACKBONE)
  1310.               {
  1311.                 doCR();
  1312.                 ShType = ACTIVE_BACKBONE;
  1313.                 getList(knownHosts,
  1314.                         "Systems that you will be an Active Backbone for",NAMESIZE, FALSE);
  1315.                 ShType = PASS_BACKBONE;
  1316.                 getList(knownHosts,
  1317.                         "Systems that you will be a Passive Backbone for",NAMESIZE, FALSE);
  1318.                 ShType = PEON;
  1319.                 getList(knownHosts,
  1320.                         "Systems that should be returned to Peon status",NAMESIZE, FALSE);
  1321.  
  1322.               }
  1323.             break;
  1324.           case 'M':
  1325.             if (WhoIsModerator(workbuf))
  1326.               {
  1327.                 if (strLen(workbuf))
  1328.                   AddData(&Moderators, NtoStrInit(thisRoom, workbuf, 0,
  1329.                                                   FALSE), NULL, TRUE);
  1330.                 else
  1331.                   KillData(&Moderators, NtoStrInit(thisRoom, "", 0, TRUE));
  1332.  
  1333.               }
  1334.             WriteAList(&Moderators, "ctdlmodr.sys", WrtNtoStr);
  1335.             doAideMessage++;
  1336.             break;
  1337.           case 'D':
  1338.             doAideMessage++;
  1339.             roomBuf.rbflags.ISDIR = getYesNo("ACTDIR");
  1340.             if (roomBuf.rbflags.ISDIR)
  1341.               {
  1342.                 if ((roomBuf.rbflags.ISDIR = getArea(&roomBuf)))
  1343.                   roomBuf.rbflags.PERMROOM = TRUE;
  1344.                 else
  1345.                   break;
  1346.  
  1347.               }
  1348.             else
  1349.               break;
  1350.           case 'U':
  1351.             doAideMessage++;
  1352.             if (c == 'U') mPrintf("load/Download room\n ");
  1353.             roomBuf.rbflags.UPLOAD   = getYesNo("UPLDAL");
  1354.             roomBuf.rbflags.DOWNLOAD = getYesNo("DNLDAL");
  1355.             if (!roomBuf.rbflags.UPLOAD && !roomBuf.rbflags.DOWNLOAD)
  1356.         Output_Citadel_Message("STRNGE",NULL, NULL, NULL);
  1357.             break;
  1358.           case 'T':
  1359.             if (roomBuf.rbflags.ISDIR)
  1360.         Output_Citadel_Message("DIRRMP", NULL, NULL, NULL);
  1361.             else
  1362.               {
  1363.                 roomBuf.rbflags.PERMROOM = !getYesNo("RMTEMP");
  1364.                 doAideMessage++;
  1365.  
  1366.               }
  1367.             break;
  1368.           case 'P':
  1369.             doAideMessage++;
  1370.             roomBuf.rbflags.PUBLIC = !getYesNo("MKRMPR");
  1371.             if (!roomBuf.rbflags.PUBLIC)
  1372.               {
  1373.                 if (getYesNo("CSNAUS"))
  1374.                   {
  1375.                     if (!wasShared || getYesNo("WRNSHR"))
  1376.                       {
  1377.                         roomBuf.rbgen = (roomBuf.rbgen + 1) % MAXGEN;
  1378.                         logBuf.lbgen[thisRoom] = (logBuf.lbgen[thisRoom]
  1379.                                                   & CALLMASK) +
  1380.                           (roomBuf.rbgen << GENSHIFT);
  1381.                         roomTab[thisRoom].rtgen = roomBuf.rbgen;
  1382.  
  1383.                       }
  1384.  
  1385.                   }
  1386.  
  1387.               }
  1388.             break;
  1389.           case 'S':
  1390.             roomBuf.rbflags.SHARED = getYesNo("NTSHRN");
  1391.             if (roomBuf.rbflags.SHARED)
  1392.               {
  1393.                 if (!wasShared)
  1394.                   {
  1395.                     roomBuf.rbflags.PERMROOM = TRUE;
  1396.                     doAideMessage++;
  1397.                /*
  1398.                 * cosmetic bug fix
  1399.                 */
  1400.                     roomTab[thisRoom].rtflags.SHARED = TRUE;
  1401.  
  1402.                   }
  1403.                 getList(addToList, wasShared ?
  1404.                         "Systems to add to the network list for this room" :
  1405.                   "Systems to network this room with", NAMESIZE, FALSE);
  1406.                 if (wasShared)
  1407.                   getList(killFromList,
  1408.                   "Systems to take off the network list", NAMESIZE, FALSE);
  1409.                 roomBuf.rbflags.AUTO_NET =
  1410.                   getYesNo("MSGSNT");
  1411.                 if (roomBuf.rbflags.AUTO_NET)
  1412.                   roomBuf.rbflags.ALL_NET =
  1413.                     getYesNo("EVNNET");
  1414.  
  1415.               }
  1416.             else
  1417.               {
  1418.                 if (wasShared)
  1419.                   doAideMessage++;
  1420.                 if (!roomBuf.rbflags.ISDIR)
  1421.                   roomBuf.rbflags.PERMROOM = FALSE;
  1422.  
  1423.               }
  1424.             break;
  1425.           case 'L':
  1426.             getList(makeKnown, "Users to be invited", NAMESIZE, FALSE);
  1427.             break;
  1428.           case 'R':
  1429.             if ((roomBuf.rbflags.READ_ONLY = getYesNo("MKREAD")))
  1430.               {
  1431.                 DoWritePrivs = TRUE;
  1432.                 getList(WritePrivs, "Users needing write privileges",   NAMESIZE, FALSE);
  1433.                 DoWritePrivs = FALSE;
  1434.                 getList(WritePrivs, "Users losing write privileges",    NAMESIZE, FALSE);
  1435.  
  1436.               }
  1437.             if (roomBuf.rbflags.READ_ONLY != wasRO)
  1438.               doAideMessage++;
  1439.             break;
  1440.           case 'O':
  1441.             doAideMessage++;
  1442.             if ((roomBuf.rbflags.INVITE = getYesNo("MKINVT")))
  1443.               {
  1444.                 roomBuf.rbflags.PUBLIC = FALSE;
  1445.                 if (getYesNo("CSNAUS"))
  1446.                   {
  1447.                     if (!wasShared || getYesNo("WRNSHR"))
  1448.                       {
  1449.                         roomBuf.rbgen = (roomBuf.rbgen + 1) % MAXGEN;
  1450.                         logBuf.lbgen[thisRoom] = (logBuf.lbgen[thisRoom] &
  1451.                                                   CALLMASK) +
  1452.                           (roomBuf.rbgen << GENSHIFT);
  1453.                         roomTab[thisRoom].rtgen = roomBuf.rbgen;
  1454.  
  1455.                       }
  1456.  
  1457.                   }
  1458.                 getList(makeKnown, "Users to be invited", NAMESIZE, FALSE);
  1459.  
  1460.               }
  1461.             break;
  1462.           case 'I':
  1463.             roomBuf.rbflags.ANON = getYesNo("MKANON");
  1464.             if (roomBuf.rbflags.ANON != wasAnon)
  1465.               doAideMessage++;
  1466.             break;
  1467.           case 'V':
  1468.             mPrintf("%s is %s.", roomBuf.rbname,
  1469.                     formatSummary(msgBuf.mbtext, TRUE));
  1470.             break;
  1471.           case 'W':
  1472.             getList(makeUnknown, "Users to be kicked out", NAMESIZE, FALSE);
  1473.             break;
  1474.           case 'Z':
  1475.             doAideMessage++;
  1476.             roomBuf.rbflags.NO_NET_DOWNLOAD =
  1477.               !getYesNo("ALOWNT");
  1478.             break;
  1479.  
  1480.         }
  1481.  
  1482.     }
  1483.   noteRoom();
  1484.   putRoom(thisRoom);
  1485.   if (doAideMessage)
  1486.     {
  1487.       ZeroMsgBuffer(&msgBuf);
  1488.       sPrintf(msgBuf.mbtext, "%s, has been edited to %s, ", buffer,
  1489.               formRoom(thisRoom, FALSE, FALSE));
  1490.       formatSummary(lbyte(msgBuf.mbtext), FALSE);
  1491.       sPrintf(lbyte(msgBuf.mbtext), ", by %s.", logBuf.lbname);
  1492.       aideMessage(NULL, FALSE);
  1493.  
  1494.     }
  1495.   free(buffer);
  1496.   return TRUE;
  1497.  
  1498. }
  1499. /*
  1500.  * * WhoIsModerator() * * This handles adding moderating-type people.
  1501.  */
  1502. char
  1503. WhoIsModerator(char *buf)
  1504. {
  1505.   if (!getXString("MODRTR", buf, NAMESIZE, "no moderator", ""))
  1506.     return FALSE;
  1507.   if (strLen(buf) != 0)
  1508.     if (findPerson(buf, &logTmp) == ERROR)
  1509.       {
  1510.       Output_Citadel_Message("NOTFND", (long)buf, NULL, NULL);
  1511.       return FALSE;
  1512.  
  1513.       }
  1514.   return TRUE;
  1515.  
  1516. }
  1517. /*
  1518.  * * formatSummary() * * This is responsible for formatting a summary of the
  1519.  * current room.
  1520.  */
  1521. char     *
  1522. formatSummary(char *buffer, char NotFinal)
  1523. {
  1524.   char     *c;
  1525.   int       size;
  1526.  
  1527.   sPrintf(buffer, "a%s, ",
  1528.           roomBuf.rbflags.INVITE ? "n Invitation only" :
  1529.           roomBuf.rbflags.PUBLIC ? public_str : private_str);
  1530.   sPrintf(lbyte(buffer), "%s",
  1531.           roomBuf.rbflags.PERMROOM ? perm_str : temp_str);
  1532.   if (roomBuf.rbflags.ARCHIVE)
  1533.     {
  1534.       sPrintf(lbyte(buffer), ", Archived ('%s', ",
  1535.               AskForNSMap(&Arch_base, thisRoom));
  1536.       if ((size = GetArchSize(thisRoom)) == 0)
  1537.         strCat(buffer, "no rollover)");
  1538.       else
  1539.         sPrintf(lbyte(buffer), "rollover at %dK)", size);
  1540.  
  1541.     }
  1542.   if (roomBuf.rbflags.ANON)
  1543.     strCat(buffer, ", Anonymous");
  1544.   if (roomBuf.rbflags.READ_ONLY)
  1545.     strCat(buffer, ", Read-Only");
  1546.   if (roomBuf.rbflags.SHARED)
  1547.     {
  1548.       strCat(buffer, ", Shared");
  1549.       if (roomBuf.rbflags.AUTO_NET)
  1550.         {
  1551.           strCat(buffer, " (autonet for ");
  1552.           strCat(buffer, roomBuf.rbflags.ALL_NET ? "all users)" :
  1553.                  "net-priv users)");
  1554.  
  1555.         }
  1556.  
  1557.     }
  1558.   if (roomBuf.rbflags.ISDIR)
  1559.     {
  1560.       strCat(buffer, ", Directory (");
  1561.       if (NotFinal || !(roomBuf.rbflags.INVITE || !roomBuf.rbflags.PUBLIC))
  1562.         {
  1563.           dirString(lbyte(buffer), &roomBuf.rbArea);
  1564.           strCat(buffer, ", ");
  1565.  
  1566.         }
  1567.       if (roomBuf.rbflags.UPLOAD)
  1568.         strCat(buffer, "uploads, ");
  1569.       if (roomBuf.rbflags.DOWNLOAD)
  1570.         strCat(buffer, "downloads, ");
  1571.       sPrintf(lbyte(buffer), "%snet downloadable",
  1572.               roomBuf.rbflags.NO_NET_DOWNLOAD ? "not " : "");
  1573.       strCat(buffer, ")");
  1574.  
  1575.     }
  1576.   strCat(buffer, " room");
  1577.   if (strLen(c = AskForNSMap(&Moderators, thisRoom)) != 0)
  1578.     sPrintf(lbyte(buffer), " (Moderator is %s)", c);
  1579.   if (roomBuf.rbflags.SHARED && NotFinal)
  1580.     ParticipatingNodes(buffer);
  1581.   return buffer;
  1582.  
  1583. }
  1584. MenuId    GetListId;
  1585.  
  1586. /**
  1587.   getList()
  1588.   This will get a list of names and blindly process them.
  1589. **/
  1590. void
  1591. getList(int (*fn) (char *data), char *prompt, int size, char Sysop)
  1592. {
  1593.   char     *buffer;
  1594.   if (cfg.BoolFlags.debug)
  1595.     splitF(NULL, " getList(%08.8lx,%s,%d,%s)\n",fn,prompt,size, (Sysop) ? "TRUE" : "FALSE");
  1596.   buffer = GetDynamic(size + 1);
  1597.   GetListId = NO_MENU;
  1598.   mPrintf("\n %s (Empty line to end)...\n", prompt);
  1599.   do
  1600.     {
  1601.       if (Sysop)  SysopPrintf(GetListId, " : ");
  1602.       else        mPrintf(" : ");
  1603.       SysopContinualString(GetListId, "", buffer, size, 0);
  1604.       if (strLen(buffer) != 0) if (!(*fn) (buffer)) break;
  1605.  
  1606.     }
  1607.   while (strLen(buffer) != 0);
  1608.   if (Sysop)
  1609.     SysopCloseContinual(GetListId);
  1610.   free(buffer);
  1611. }
  1612. /*
  1613.  * * replaceString() * * This function corrects typos in message entry.
  1614.  */
  1615. #define REPLACE_SIZE    2000
  1616. void
  1617. replaceString(char *buf, int lim, char Global)
  1618. {
  1619.   char      oldString[2 * SECTSIZE];
  1620.   char     *newString;            /*
  1621.  
  1622.                                    *
  1623.                                    * * Eliminate stack overflows in Turbo c
  1624.                                    */
  1625.   char     *loc,
  1626.            *oldloc,
  1627.            *textEnd;
  1628.   char     *pc;
  1629.   int       incr,
  1630.             length,
  1631.             oldLen,
  1632.             newLen,
  1633.             maxLen;
  1634.  
  1635. /*
  1636.  * find terminal null
  1637.  */
  1638.   textEnd = lbyte(buf);
  1639.   length = strLen(buf);
  1640.   getString("GETSTR", oldString, (2 * SECTSIZE), 0);
  1641.   if ((oldLen = strLen(oldString)) == 0)
  1642.     return;
  1643.   if ((oldloc = loc = matchString(buf, oldString, textEnd)) == NULL)
  1644.     {
  1645.       mPrintf("Not found...\n ");
  1646.       return;
  1647.  
  1648.     }
  1649. /*
  1650.  * so we never have too long of a replacement string (unless Global R.)
  1651.  */
  1652.   maxLen = minimum(REPLACE_SIZE, (MAXTEXT - (length - oldLen)));
  1653.   newString = GetDynamic(REPLACE_SIZE);
  1654.   getString("GETREP", newString, maxLen, 0);
  1655.   newLen = strLen(newString);
  1656.   do
  1657.     {
  1658.       textEnd = lbyte(buf);
  1659.       length = textEnd - buf;
  1660.       if ((newLen - oldLen) >= lim - length)
  1661.         {
  1662.           mPrintf("Overflow!!\n ");
  1663.           free(newString);
  1664.           return;
  1665.  
  1666.         }
  1667.  /*
  1668.   * delete old string:
  1669.   */
  1670.       for (pc = loc,
  1671.            incr = strLen(oldString);
  1672.            *pc = *(pc + incr);  /*
  1673.                                  * Compiler generates a warning for this
  1674.                                  * line
  1675.                                  */
  1676.            pc++) ;
  1677.       textEnd -= incr;
  1678.  /*
  1679.   * make room for new string:
  1680.   */
  1681.       for (pc = textEnd, incr = strLen(newString); pc >= loc; pc--)
  1682.         {
  1683.           *(pc + incr) = *pc;
  1684.  
  1685.         }
  1686.  /*
  1687.   * insert new string:
  1688.   */
  1689.       for (pc = newString; *pc; *loc++ = *pc++) ;
  1690.       if (Global)
  1691.         oldloc = loc = matchString(buf, oldString, oldloc);
  1692.  
  1693.     }
  1694.   while (Global && oldloc != NULL);
  1695.   free(newString);
  1696.  
  1697. }
  1698. /*
  1699.  * * initialArchive() * * This function is responsible for doing an initial
  1700.  * archive of a room.
  1701.  */
  1702. void
  1703. initialArchive(char *fn)
  1704. {
  1705.   char     *TmpMsg,
  1706.            *realfn;
  1707.  
  1708.   TmpMsg = strdup(msgBuf.mbtext);
  1709.   realfn = GetDynamic(strLen(fn) + 15);
  1710.   TranslateFilename(realfn, fn);
  1711.   mPrintf("Doing the initial archive...\n");
  1712.   msgToDisk(realfn, TRUE, 0l, 0l, 0);
  1713.   strCpy(msgBuf.mbtext, TmpMsg);
  1714.   free(TmpMsg);
  1715.   free(realfn);
  1716.  
  1717. }
  1718. /*
  1719.  * * knownHosts() * * This function handles setting systems as hosts.
  1720.  */
  1721. int
  1722. knownHosts(char *name)
  1723. {
  1724.   int       slot,
  1725.             i;
  1726.  
  1727.   if ((slot = CmnNetList(name, &i, ERROR, "does not share this room with you"))
  1728.       == ERROR)
  1729.     return TRUE;
  1730.   if (i == ERROR)
  1731.     if ((i = ListAsShared(name)) == ERROR)
  1732.       return TRUE;
  1733.   SetMode(netBuf.netRooms[i].mode, ShType);
  1734.   putNet(slot, &netBuf);
  1735.   return TRUE;
  1736.  
  1737. }
  1738. /*
  1739.  * * addToList() * * This function will Add a system to a room networking
  1740.  * list.
  1741.  */
  1742. int
  1743. addToList(char *name)
  1744. {
  1745.   int       slot,
  1746.             i;
  1747.  
  1748.   if ((slot = CmnNetList(name, &i, FALSE, "already networks this room with you"))
  1749.       != ERROR)
  1750.     {
  1751.       if (ListAsShared(name) != ERROR)
  1752.         {
  1753.           putNet(slot, &netBuf);
  1754.  
  1755.         }
  1756.  
  1757.     }
  1758.   return TRUE;
  1759.  
  1760. }
  1761. /*
  1762.  * * ListAsShared() * * This function will find an empty share slot.
  1763.  */
  1764. int
  1765. ListAsShared(char *name)
  1766. {
  1767.   int       i,
  1768.             temp,
  1769.             gen;
  1770.  
  1771.   for (i = 0; i < SHARED_ROOMS; i++)
  1772.     {
  1773.       if ((netBuf.netRooms[i].srgen & 0x8000) != 0)
  1774.         {
  1775.      /*
  1776.       * Salvage attempt
  1777.       */
  1778.           temp = netBuf.netRooms[i].srslot & 0x7fff;
  1779.           gen = netBuf.netRooms[i].srgen & 0x7fff;
  1780.           if (roomTab[temp].rtgen != gen ||     /*
  1781.                                                  * No longer exists!
  1782.                                                  */
  1783.               roomTab[temp].rtflags.SHARED == 0 ||      /*
  1784.                                                          * Not netting
  1785.                                                          */
  1786.               !roomTab[temp].rtflags.INUSE)
  1787.             {
  1788.          /*
  1789.           * no longer exists
  1790.           */
  1791.               break;
  1792.  
  1793.             }
  1794.  
  1795.         }
  1796.       else
  1797.         break;
  1798.  
  1799.     }
  1800.   if (i == SHARED_ROOMS)
  1801.     {
  1802.       mPrintf("Sorry, already sharing %d rooms with %s.\n", SHARED_ROOMS, name);
  1803.       return ERROR;
  1804.  
  1805.     }
  1806.   netBuf.netRooms[i].srslot = thisRoom;
  1807.   netBuf.netRooms[i].lastMess = cfg.newest;
  1808.   netBuf.netRooms[i].srgen = roomBuf.rbgen + (unsigned) 0x8000;
  1809.   SetMode(netBuf.netRooms[i].mode, PEON);
  1810.   return i;
  1811.  
  1812. }
  1813. /*
  1814.  * * searchForRoom() * * This function checks to see if the current room is
  1815.  * in the current node's * room sharing list.
  1816.  */
  1817. int
  1818. searchForRoom()
  1819. {
  1820.   RoomSearch data;
  1821.  
  1822.   strCpy(data.Room, roomBuf.rbname);
  1823.   EachSharedRoom(thisNet, IsRoomRoutable, NULL, &data);
  1824.   if (data.reason == FOUND)
  1825.     return data.index;
  1826.   return ERROR;
  1827.  
  1828. }
  1829. /*
  1830.  * * getXString()
  1831.  */
  1832. char
  1833. getXString(char *prompt, char *target, int targetSize, char *CR_str,
  1834.            char *dft)
  1835. {
  1836.   return getXInternal(NO_MENU, prompt, target, targetSize, CR_str, dft);
  1837.  
  1838. }
  1839. /*
  1840.  * * getXInternal() * * This is a sophisticated GetString().  Will return
  1841.  * TRUE and FALSE -- read * the code to figure it out.
  1842.  */
  1843. char
  1844. getXInternal(MenuId id, char *prompt, char *target, int targetSize,
  1845.              char *CR_str, char *dft)
  1846. {
  1847.   if (CR_str != NULL && strLen(CR_str) != 0)
  1848.     mPrintf( "C/R = '%s',", CR_str);
  1849.   mPrintf("(ESCape to abort)");
  1850.   exChar = ESC;
  1851.   SysopContinualString(id, prompt, target, targetSize, QUEST_SPECIAL);
  1852.   exChar = '?';
  1853.   if (!onLine())
  1854.     return FALSE;       /*
  1855.                          * Lost carrier
  1856.                          */
  1857.   if (target[0] == ESC)
  1858.     return FALSE;
  1859.   if (CR_str == NULL && target[0] == 0)
  1860.     return FALSE;
  1861.   else if (target[0] == 0)
  1862.     strCpy(target, dft);
  1863.   return TRUE;
  1864.  
  1865. }
  1866. /*
  1867.  * * makeKnown() * * This makes a user knowledgeable about this room.
  1868.  */
  1869. int
  1870. makeKnown(char *user)
  1871. {
  1872.   return doMakeWork(user, roomBuf.rbgen);
  1873.  
  1874. }
  1875. /*
  1876.  * * makeUnknown() * * This makes a user forget about this room.
  1877.  */
  1878. int
  1879. makeUnknown(char *user)
  1880. {
  1881.   return doMakeWork(user, (roomBuf.rbgen + (MAXGEN - 1)) % MAXGEN);
  1882.  
  1883. }
  1884. /*
  1885.  * * doMakeWork() * * This is a worker function.
  1886.  */
  1887. int
  1888. doMakeWork(char *user, int val)
  1889. {
  1890.   int       target;
  1891.  
  1892.   if ((target = findPerson(user, &logTmp)) == ERROR)
  1893.     mPrintf("'%s' not found.\n", user);
  1894.   else
  1895.     {
  1896.       logTmp.lbgen[thisRoom] = (val << GENSHIFT) + MAXVISIT - 1;
  1897.       putLog(&logTmp, target);
  1898.  
  1899.     }
  1900.   return TRUE;
  1901.  
  1902. }
  1903. /*
  1904.  * * killFromList() * * Kills systems from sharing a room.
  1905.  */
  1906. int
  1907. killFromList(char *sysName)
  1908. {
  1909.   int       i,
  1910.             slot;
  1911.  
  1912.   if ((slot = CmnNetList(sysName, &i, TRUE, "does not network this room with you"))
  1913.       == ERROR)
  1914.     return TRUE;
  1915.   netBuf.netRooms[i].srgen = 0;
  1916.   putNet(slot, &netBuf);
  1917.   return TRUE;
  1918.  
  1919. }
  1920. /*
  1921.  * * CmnNetList() * * This does general work on net stuff.
  1922.  */
  1923. int
  1924. CmnNetList(char *name, int *slot, char ShouldBeThere, char *errstr)
  1925. {
  1926.   extern int thisNet;
  1927.  
  1928. /*
  1929.  * This will do required getNet() if it returns true
  1930.  */
  1931.   if (!ReqNodeName("", name, NULL, FALSE, TRUE, FALSE, FALSE, FALSE, &netBuf))
  1932.     return ERROR;
  1933.   *slot = searchForRoom();
  1934.   if ((*slot == ERROR && ShouldBeThere == TRUE) ||
  1935.       (*slot != ERROR && ShouldBeThere == FALSE))
  1936.     {
  1937.       mPrintf("%s %s.", name, errstr);
  1938.       doCR();
  1939.       return ERROR;
  1940.  
  1941.     }
  1942.   return thisNet;
  1943.  
  1944. }
  1945. /*
  1946.  * * WritePrivs() * * Here we give or take away write privs.
  1947.  */
  1948. int
  1949. WritePrivs(char *user)
  1950. {
  1951.   return doMakeWork(user, (DoWritePrivs) ?
  1952.                  (((roomBuf.rbgen + RO_OFFSET)) % MAXGEN) : roomBuf.rbgen);
  1953.  
  1954. }
  1955.